package com.bidevalution.service.impl;

import com.bidevalution.core.constant.CharConstant;
import com.bidevalution.core.enums.ApiEnum;
import com.bidevalution.core.enums.FileStatusEnum;
import com.bidevalution.core.enums.FileTypeEnum;
import com.bidevalution.core.enums.TbFileErrorEnum;
import com.bidevalution.dao.ApiBidFileMapper;
import com.bidevalution.dao.ApiSectionEnterpriseMapper;
import com.bidevalution.dao.ApiSectionInfoMapper;
import com.bidevalution.model.BidFile;
import com.bidevalution.model.SectionEnterprise;
import com.bidevalution.model.SectionInfo;
import com.bidevalution.properties.SecurityProperties;
import com.bidevalution.service.TbFileService;
import com.qtp.core.common.RemoteService;
import com.qtp.core.model.RemoteResponse;
import com.qtp.core.model.Response;
import com.qtp.core.util.GsonUtil;
import com.qtp.core.util.Md5Util;
import com.qtp.core.util.OConvertUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author huyiqi
 * @date 2019/11/12
 */
@EnableConfigurationProperties(SecurityProperties.class)
@Service(value = "tbFileService")
public class TbFileServiceImpl extends BaseServiceImpl implements TbFileService {

    private static final Logger log = LoggerFactory.getLogger(TbFileServiceImpl.class);

    private static final String NEED_SLEEP_KEY = "needSleep";
    private static final String STILL_POLLING_KEY = "stillPolling";
    private static final String DOWNLOAD_RESULT_STATUS_KEY = "status";
    private static final Integer SECTION_STATUS_BEGIN = 2;

    /**
     * 获取应推完投标文件的单位信息并插入数据库
     * @param sectionId 标段id
     * @param sectionCode 标段编码
     */
    @SuppressWarnings("unchecked")
    @Async("asyncServiceExecutor")
    @Override
    public void getEnterFilePushed(String sectionId, String sectionCode) {
        log.info("======投标文件的线程已经开启======");
        log.info("======项目编码："+sectionCode+"======");
        boolean pollingToCheckFile = true;
        while (pollingToCheckFile) {
            try {
                if ("districtCounty".equals(securityProperties.getDistrictCounty())) {
                    pollingToCheckFile = getDistrictAndCountyEnter(sectionId, sectionCode);
                } else {
                    Map<String, Boolean> result = getOneEnterFilePushed(sectionId, sectionCode);
                    pollingToCheckFile = result.get(STILL_POLLING_KEY);
                    if(result.get(NEED_SLEEP_KEY)) {
                        Thread.sleep(5000);
                        log.info("休息了5秒");
                    }
                }
            } catch (Exception e) {
                log.error("获取投标单位信息失败", e);
            }
        }
        log.info("======投标文件的线程end,项目编码："+sectionCode+"======");
    }

    private Response downloadFile(Map<String, Object> map, String sectionCode, String guid) {
        log.info("准备触发下载单位的投标文件，单位guid："+guid);
        RemoteResponse resp = RemoteService.doPost(securityProperties.getDownInformUrl(), map);
        log.info("触发完成，单位guid："+guid);
        log.info("status:"+resp.getStatus());
        log.info("content:"+resp.getContent());
        String content = resp.getContent();
        Map<String, Object> result = GsonUtil.jsonToMapWithGson(content);
        try {
            if (result != null && result.size() > 0 && result.containsKey(DOWNLOAD_RESULT_STATUS_KEY)) {
                if (!result.get(DOWNLOAD_RESULT_STATUS_KEY).equals(CharConstant.CHAR_TRUE)) {
                    String error = OConvertUtils.getString(result.get("error"));
                    askRePushFile(sectionCode, guid, error);
                    Thread.sleep(2000);
                    return Response.fail(error);
                }
            } else {
                askRePushFile(sectionCode, guid, "下载工具调用失败！");
                Thread.sleep(2000);
                return Response.fail("下载工具调用失败！");
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return Response.fail(e.getMessage());
        }

        return Response.ok();
    }

    @SuppressWarnings("unchecked")
    private boolean getDistrictAndCountyEnter(String sectionId, String sectionCode) throws Exception {
        SectionInfo sectionInfo = sectionInfoMapper.getSectionInfoByCode(sectionCode);
        Integer reviewState = sectionInfo.getReviewState();
        if(SECTION_STATUS_BEGIN.equals(reviewState)) {
            return false;
        }//调用接口
        ApiEnum api = ApiEnum.GET_ENTER_FILE_TO_BID;
        String token = api.getToken();
        Map<String, Object> resultMap = null;
        try {
            resultMap = invokeJyWebServiceWithGbkResult(api, token, sectionCode);

        } catch (Exception e) {
            log.error("调用哪个投标单位信息接口时发生错误：" + e);
        }
        //判断调用结果
        if (resultMap != null) {
            Response<Map<String, Object>> execResult = jYApiSuccess(resultMap);
            if (execResult.isSuccess()) {
                Map<String, Object> bidEnter = (Map<String, Object>) execResult.getResult().get("BidEnter");
                if (bidEnter != null) {
                    //校验MD5
                    String filePath = OConvertUtils.getString(bidEnter.get("FileAddr"));
                    String fileMD5 = OConvertUtils.getString(bidEnter.get("FileMD5"));
                    String guid = OConvertUtils.getString(bidEnter.get("GUID"));
                    //保存文件操作信息
                    String fileName = getFileName(filePath);
                    String ftpFileName = String.format("%s/%s", guid, fileName);
                    ftpFileName = getFtpFilePath(ftpFileName);
                    int status = FileStatusEnum.NOT_DOWNLOAD.getStatus();
                    String fileId = saveBidFile(fileName, ftpFileName, guid, FileTypeEnum.TB.getType(), sectionCode, status, bidFileMapper);
                    //保存投标单位信息
                    saveBidEnter(sectionId, bidEnter, status);
                    //下载投标文件
                    Map<String, Object> map = new HashMap<>(8);
                    map.put("id", fileId);
                    map.put("guid", guid);
                    map.put("fileUrl", filePath);
                    map.put("sectionCode", sectionCode);
                    map.put("fileMD5", fileMD5);
                    map.put("fileName", fileName);
                    map.put("fileDownPath", getFtpFilePath(guid));
                    Response response = downloadFile(map, sectionCode, guid);
                    if (!response.isSuccess()) {
                        throw new Exception();
                    }
                    log.info("投标文件保存成功");
                }
            } else {
                log.error("获取投标信息的接口调用失败：" + execResult.getError());
            }
        }
        return true;
    }

    @SuppressWarnings("unchecked")
    private Map<String, Boolean> getOneEnterFilePushed(String sectionId, String sectionCode) {
        boolean needSleep = true;
        SectionInfo sectionInfo = sectionInfoMapper.getSectionInfoByCode(sectionCode);
        Integer reviewState = sectionInfo.getReviewState();
        if(SECTION_STATUS_BEGIN.equals(reviewState)) {
            return buildResult(false, false);
        }
        //调用接口
        ApiEnum api = ApiEnum.GET_ENTER_FILE_PUSHED;
        String token = api.getToken();
        Map<String, Object> resultMap = null;
        try {
            resultMap = invokeJyWebServiceWithGbkResult(api, token, sectionCode);
        } catch (Exception e) {
            log.error("掉用哪个投标单位信息接口时发生错误：" + e);
        }
        //判断调用结果
        if (resultMap != null) {
            Response<Map<String, Object>> execResult = jYApiSuccess(resultMap);
            if (execResult.isSuccess()) {
                Map<String, Object> bidEnter = (Map<String, Object>) execResult.getResult().get("BidEnter");
                if(bidEnter != null) {
                    //如果能获取到单位，每次轮询后就不等待，直接去获取下一家单位
                    needSleep = false;
                    //校验MD5
                    String filePath = OConvertUtils.getString(bidEnter.get("FileAddr"));
                    String fileMD5 = OConvertUtils.getString(bidEnter.get("FileMD5"));
                    String guid = OConvertUtils.getString(bidEnter.get("GUID"));
                    Response<String> md5Rsp = null;
                    try {
                        md5Rsp = checkFileMD5(filePath, fileMD5);
                    } catch (FileNotFoundException e) {
                        log.error("检查MD5失败，文件未找到");
                    }

                    //保存投标单位信息
                    if (md5Rsp != null && md5Rsp.isSuccess()) {
                        //保存文件操作信息
                        String fileName = getFileName(filePath);
                        String ftpFileName = getFtpFilePath(filePath);
                        int status = FileStatusEnum.DOWNLOAD_SUCCESS.getStatus();
                        saveBidFile(fileName, ftpFileName, guid, FileTypeEnum.TB.getType(), sectionCode, status, bidFileMapper);
                        //保存投标单位信息
                        saveBidEnter(sectionId, bidEnter, status);
                        log.info("投标文件校验成功，bidFile保存成功");
                    } else {
                        log.error("MD5校验失败，请求重推，filePath:" + filePath);
                        //如果文件校验失败，就请求重推文件
                        try {
                            askRePushFile(sectionCode, guid, "MD5校验失败");
                        } catch (Exception e) {
                            log.error("重推投标文件接口调用失败：", e);
                        }
                    }
                }
            } else {
                log.error("获取投标信息的接口调用失败：" + execResult.getError());
            }
        } else {
            log.error("接口返回值为空");
        }
        return buildResult(needSleep, true);
    }

    /**
     * 检查文件的MD5是否和给的相同
     * @param filePath 文件相对路径
     * @param fileMD5 文件MD5
     * @return 返回校验结果
     */
    private Response<String> checkFileMD5(String filePath, String fileMD5) throws FileNotFoundException {
        File file = new File(getFtpFilePath(filePath));
        if(file.exists()) {
            InputStream is = null;
            try {
                is = new FileInputStream(file);
                String md5 = Md5Util.getMD5(is);
                log.info("算出来的MD5："+md5);
                log.info("接口取到的MD5："+fileMD5);
                if(md5.equalsIgnoreCase(fileMD5)) {
                    return Response.ok("验证通过");
                } else {
                    return Response.fail(TbFileErrorEnum.INVALID_MD5.getMsg(), TbFileErrorEnum.INVALID_MD5.getCode());
                }
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        log.error("流关闭出错!");
                    }
                }
            }
        } else {
            return Response.fail(TbFileErrorEnum.FILE_NOT_EXISTS.getMsg(), TbFileErrorEnum.FILE_NOT_EXISTS.getCode());
        }
    }

    /**
     * 保存单位信息
     * @param sectionId 标段id
     * @param bidEnter 单位信息
     * @return 返回是否成功
     */
    private Response<String> saveBidEnter(String sectionId, Map<String, Object> bidEnter, int status) {
        String enterpriseName = OConvertUtils.getString(bidEnter.get("EnterpriseName"));
        log.info("查询是否已有单位信息，sectionId："+sectionId+"，enterpriseName："+enterpriseName);
        //如果接口给的单位名称与文件中的单位名称不一致，会重复插数据
        SectionEnterprise enterprise = enterpriseMapper.findOne(sectionId, enterpriseName);
        if(enterprise == null) {
            log.info("没有查询到，准备插入，参数如下：seq_id,section_id,pt_ent_id,name,filename,manager,idcard,createtime,bidfilestate");

            String fileAddr = OConvertUtils.getString(bidEnter.get("FileAddr"));
            String personnelName = OConvertUtils.getString(bidEnter.get("PersonnelName"));
            String idCardNum = OConvertUtils.getString(bidEnter.get("IDCardNum"));
            String guid = OConvertUtils.getString(bidEnter.get("GUID"));
            String seqId = UUID.randomUUID().toString();
            Date date = new Date();
            Map<String, Object> map = new HashMap<>(16);
            map.put("seqId", seqId);
            map.put("sectionId", sectionId);
            map.put("ptEnterpriseId", guid);
            map.put("enterpriseName", enterpriseName);
            map.put("bidfileName", getFileName(fileAddr));
            map.put("manager", personnelName);
            map.put("managerIdCard", idCardNum);
            map.put("createTime", date);
            map.put("bidFileState", status);
            log.info(seqId+","+sectionId+","+guid+","+enterpriseName+","+getFileName(fileAddr)+","+personnelName+","+idCardNum
                    +","+date+","+ FileStatusEnum.DOWNLOAD_SUCCESS.getStatus());
            enterpriseMapper.insert(map);
            log.info("插入成功");
        } else {
            log.info("查询到已有企业，不插入，seq_id："+enterprise.getSeqId());
        }
        return Response.ok();
    }

    /**
     * 请求重推附件
     * @param sectionCode 标段编码
     * @param guid 企业id
     * @param remark 说明
     * @return 返回地调用结果
     */
    @Override
    public Response<String> askRePushFile(String sectionCode, String guid, String remark) throws Exception {
        //调用接口
        ApiEnum api = ApiEnum.ASK_RE_PUSH_FILE;
        String token = api.getToken();
        Map<String, Object> resultMap = invokeJyWebService(api, token, sectionCode, guid, remark);
        //判断调用结果
        return returnExecResult(resultMap, API_TYPE_JY);
    }
    private Map<String, Boolean> buildResult(boolean needSleep, boolean stillPolling) {
        Map<String, Boolean> result = new HashMap<>(4);
        result.put(NEED_SLEEP_KEY, needSleep);
        result.put(STILL_POLLING_KEY, stillPolling);
        return result;
    }

    /**
     * 反馈投标文件导入情况
     * @param projId 招标项目ID
     * @param ptEnterpriseId 平台企业id（建易给的）
     * @param result 导入结果：0失败，1成功
     * @param errorCode 错误码（自定义）
     * @param description 描述信息
     * @return 返回调用结果
     */
    @Override
    public Response<String> backTenderingDocuments(String projId, String ptEnterpriseId, String result, String errorCode
            , String description) throws Exception {
        Map<String, Object> map = sectionInfoMapper.getCodeByProjId(projId);
        String sectionCode = null;
        if(map != null) {
            sectionCode = OConvertUtils.getString(map.get("section_code"));
        }
        if(StringUtils.isBlank(sectionCode)) {
            return Response.fail("无法根据projId查询到sectionCode");
        }
        //调用接口
        ApiEnum api = ApiEnum.BACK_PUT_IN_RESULT;
        String token = api.getToken();
        Map<String, Object> resultMap = invokeJyWebService(api, token, sectionCode, ptEnterpriseId, result, errorCode
                , description);
        //判断调用结果
        return returnExecResult(resultMap, API_TYPE_JY);
    }

    @Override
    public void updateFileDownResult(String id) {
        BidFile bidFile = bidFileMapper.getBidFileById(id);
        if (bidFile != null) {
            int status = FileStatusEnum.DOWNLOAD_SUCCESS.getStatus();
            bidFile.setStatus(status);
            bidFileMapper.update(bidFile);
            Map<String, Object> map = new HashMap<>(4);
            map.put("ptEnterpriseId", bidFile.getBidEntId());
            map.put("bidFileState", status);
            enterpriseMapper.updateBidFileState(map);
        }

    }

    @Autowired(required = false)
    private ApiSectionEnterpriseMapper enterpriseMapper;
    @Autowired(required = false)
    private ApiBidFileMapper bidFileMapper;
    @Autowired(required = false)
    private ApiSectionInfoMapper sectionInfoMapper;
    @Autowired(required = false)
    private SecurityProperties securityProperties;
}
